﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Text;
using System.Windows.Forms;
using BGShell.Collections;

namespace BGShell.Components
{
    internal sealed class KeyboardHookInfoFile : IDisposable
    {
        private readonly HashSet<Keys> _keys = new HashSet<Keys>();
        
        private readonly List<String> _strings = new List<String>();
        private readonly Collection<String> _stringCollection;

        private IntPtr _handle;

        private String _dir;
        private String _path;
        private String _pathTemp;

        private FileStream _file;
        private BinaryWriter _writer;

        public KeyboardHookInfoFile()
        {
            _stringCollection = new Collection<String>(_strings);
        }

        public IntPtr Handle
        {
            get { return _handle; }
            set
            {
                const string FileNameFormatString = "HookInfo-{0}-0x{1:x8}.bin";

                // IntPtr doesn't support x8 format specifier
                var fileName = String.Format(CultureInfo.InvariantCulture,
                    FileNameFormatString, CurrentSessionId, value.ToInt64());

                _dir = Path.Combine(LocalAppData, "BGShell");
                _path = Path.Combine(_dir, fileName);
                _pathTemp = _path + ".tmp";

                _handle = value;
            }
        }

        public void Dispose()
        {
            if (_writer != null)
            {
                _writer.Close();
                _writer = null;
            }

            if (_file != null)
            {
                _file.Close();
                _file = null;
            }
        }

        public void Save()
        {
            Dispose();

            if (!Directory.Exists(_dir))
            {
                Directory.CreateDirectory(_dir);
            }

            _file = OpenFile(_pathTemp);
            _writer = new BinaryWriter(_file, Utf16NoBom);

            foreach (var key in _keys)
            {
                _writer.Write((int)(key));
            }

            _writer.Write(0);

            foreach (var str in _strings)
            {
                _writer.Write(str);
                _writer.Write(0);
            }

            _writer.Write(0);
            _writer.Flush();

            if (File.Exists(_path))
            {
                File.Delete(_path);
            }

            File.Move(_pathTemp, _path);
        }

        public HashSet<Keys> ShortcutKeys
        {
            get { return _keys; }
        }

        public Collection<String> Strings
        {
            get { return _stringCollection; }
        }

        private static int CurrentSessionId
        {
            get
            {
                using (var process = Process.GetCurrentProcess())
                {
                    return process.SessionId;
                }
            }
        }

        private static string LocalAppData
        {
            get
            {
                return Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
            }
        }

        private static FileStream OpenFile(string path)
        {
            return new FileStream(path,
                                  FileMode.Create,
                                  FileAccess.Write,
                                  FileShare.Read | FileShare.Delete,
                                  0x1000,
                                  FileOptions.DeleteOnClose | FileOptions.SequentialScan);
        }

        private static readonly Encoding Utf16NoBom = new UnicodeEncoding(false, false);
    }
}
